#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "galois.h"
#include "galois_add.h"
#include <sys/time.h>

#define BUFSIZE (50000)
#define S_BUFSIZE (4096)
#define SPLITCOMP2 1
#define SPLITCOMP4 2

extern int printer;
int printer;

void usage(char *s)
{
  fprintf(stderr, "usage: gf_basic_tester w splitcomp2|splitcomp4 ntrials-for-correctness\n");
  if (s != NULL) fprintf(stderr, "%s\n", s);
  exit(1);
}

main(int argc, char **argv)
{
  unsigned int w, alltype, nt, i, x, y, z, top, elt, q1, q2;
  unsigned int *b1, *b2;
  unsigned int *ib1, *ib2, *ib3;
  unsigned short *sb1, *sb2, *sb3;
  struct timeval t1, t2;
  struct timezone tz;
  long t0, now;
  double total, tsec;
  unsigned char *cb1, *cb2, *cb3;
  int mtype;

  if (argc != 4) usage(NULL);

  w = atoi(argv[1]);
  if (!(w == 16 || w == 32)) usage("w must be in 16 or 32 (for splitcomp2) or 32 (splitcomp4)\n");

  
/*  Table initialization */
  if (strcmp(argv[2], "splitcomp2") == 0) {
    mtype = SPLITCOMP2;
  } else if (strcmp(argv[2], "splitcomp4") == 0) {
    mtype = SPLITCOMP4;
	} else {
    usage("arg 2 must be splitcomp2 or splitcomp4\n");
  }
 
  nt = atoi(argv[3]);

  t0 = time(0);
  srand48(t0); 
  printf("W: %d\n", w);
  printf("Method: %s\n", argv[2]);
  printf("Seeding random number generator with %u\n", t0);
  
  top = (1 << w);

  b1 = (unsigned int *) malloc(sizeof(unsigned int) * S_BUFSIZE);
  b2 = (unsigned int *) malloc(sizeof(unsigned int) * S_BUFSIZE);
  if (w < 32 ) {
    for (i = 0; i < S_BUFSIZE; i++) b1[i] = lrand48() % top;
    for (i = 0; i < S_BUFSIZE; i++) b2[i] = lrand48() % top;
  } else {
    for (i = 0; i < S_BUFSIZE; i++) b1[i] = lrand48();
    for (i = 0; i < S_BUFSIZE; i++) b2[i] = lrand48();
  }

  if (w == 8 || w == 16 || w == 32) {
    cb1 = (unsigned char *) malloc(sizeof(unsigned char) * BUFSIZE);
    cb2 = (unsigned char *) malloc(sizeof(unsigned char) * BUFSIZE);
    cb3 = (unsigned char *) malloc(sizeof(unsigned char) * BUFSIZE);
    for (i = 0; i < BUFSIZE; i++) cb1[i] = lrand48() % 256;
    for (i = 0; i < BUFSIZE; i++) cb2[i] = lrand48() % 256;
  }

	sb1 = (unsigned short *) cb1;
	sb2 = (unsigned short *) cb2;
	sb3 = (unsigned short *) cb3;
	ib1 = (unsigned int *) cb1;
	ib2 = (unsigned int *) cb2;
	ib3 = (unsigned int *) cb3;


	if (w == 16 && mtype == SPLITCOMP2) {
		printf("Doing 10 seconds worth of region_multiplies - Three tests:\n");
		printf("   Test 0: Overwrite initial region\n");
		printf("   Test 1: Products to new region\n");
		printf("   Test 2: XOR products into second region\n\n");

		for (i = 0; i < 3; i++) {
			t0 = time(0);
			gettimeofday(&t1, &tz);
			total = 0;
			while (time(0) - t0 < 10) {
				for (x = 0; x < 10; x++) {
					for (elt = 0; elt == 0; elt = lrand48()%top) ;
					if (i == 0) {
						galois_split_composite_w16_region_mult_k2((unsigned char *) sb1, elt, BUFSIZE, NULL, 0); 
						} else if (i == 1) {
						galois_split_composite_w16_region_mult_k2((unsigned char *) sb1, elt, BUFSIZE, (unsigned char *) sb3, 0); 
					} else {
						galois_split_composite_w16_region_mult_k2((unsigned char *) sb1, elt, BUFSIZE, (unsigned char *) sb3, 1); 
					}
					total += BUFSIZE;
				}
				}
				gettimeofday(&t2, &tz);

				tsec = 0;
				tsec += t2.tv_usec;
				tsec -= t1.tv_usec;
				tsec /= 1000000.0;
				tsec += t2.tv_sec;
				tsec -= t1.tv_sec;
				printf("   Test %u: %.5lf Megabytes of Multiplies per second\n", i, total/tsec/1024.0/1024.0);
			}
		}


		if (w == 32 && mtype == SPLITCOMP4) {
			printf("Doing 10 seconds worth of region_multiplies - Three tests:\n");
			printf("   Test 0: Overwrite initial region\n");
			printf("   Test 1: Products to new region\n");
			printf("   Test 2: XOR products into second region\n\n");

			for (i = 0; i < 3; i++) {
				t0 = time(0);
				gettimeofday(&t1, &tz);
				total = 0;
				while (time(0) - t0 < 10) {
					for (x = 0; x < 10; x++) {
						for (elt = 0; elt == 0; elt = lrand48()) ;
							if (i == 0) {
							galois_split_composite_w32_region_mult_k4((unsigned char *)ib1, elt, BUFSIZE, NULL, 0); 
							} else if (i == 1) {
							galois_split_composite_w32_region_mult_k4((unsigned char *)ib1, elt, BUFSIZE, (unsigned char *)ib3, 0); 
						} else if (i == 2) {
							galois_split_composite_w32_region_mult_k4((unsigned char *)ib1, elt, BUFSIZE, (unsigned char *)ib3, 1); 
						}
						total += BUFSIZE;
					}
					}
					gettimeofday(&t2, &tz);

					tsec = 0;
					tsec += t2.tv_usec;
					tsec -= t1.tv_usec;
					tsec /= 1000000.0;
					tsec += t2.tv_sec;
					tsec -= t1.tv_sec;
					printf("   Test %u: %.5lf Megabytes of Multiplies per second\n", i, total/tsec/1024.0/1024.0);
				}
			}


			if (w == 32 && mtype == SPLITCOMP2) {
				printf("Doing 10 seconds worth of region_multiplies - Three tests:\n");
				printf("   Test 0: Overwrite initial region\n");
				printf("   Test 1: Products to new region\n");
				printf("   Test 2: XOR products into second region\n\n");

				for (i = 0; i < 3; i++) {
					t0 = time(0);
					gettimeofday(&t1, &tz);
					total = 0;
					while (time(0) - t0 < 10) {
						for (x = 0; x < 10; x++) {
							for (elt = 0; elt == 0; elt = lrand48()) ;
								if (i == 0) {
								galois_split_composite_w32_region_mult_k2((unsigned char *)ib1, elt, BUFSIZE, NULL, 0); 
								} else if (i == 1) {
								galois_split_composite_w32_region_mult_k2((unsigned char *)ib1, elt, BUFSIZE, (unsigned char *)ib3, 0); 
							} else if (i == 2) {
								galois_split_composite_w32_region_mult_k2((unsigned char *)ib1, elt, BUFSIZE, (unsigned char *)ib3, 1); 
							}
							total += BUFSIZE;
						}
						}
						gettimeofday(&t2, &tz);

						tsec = 0;
						tsec += t2.tv_usec;
						tsec -= t1.tv_usec;
						tsec /= 1000000.0;
						tsec += t2.tv_sec;
						tsec -= t1.tv_sec;
						printf("   Test %u: %.5lf Megabytes of Multiplies per second\n", i, total/tsec/1024.0/1024.0);
					}
				}
			}
